package com.wuage.infrastructure.security;

import com.wuage.infrastructure.status.BizResultCode;
import com.wuage.infrastructure.utils.JwtUtils;
import com.wuage.infrastructure.utils.OpenGsonWrapper;
import com.wuage.infrastructure.utils.Response;
import com.wuage.interfaces.auth.domain.LoginUserDO;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.stereotype.Component;

import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;
import java.util.Objects;

/**
 * @author Shynin
 * @version 1.0
 * @date 2020-09-02 23:55
 * 验证用户名密码正确后，生成一个token，并将token返回给客户端
 * 该类继承自UsernamePasswordAuthenticationFilter，重写了其中的2个方法 ,
 * attemptAuthentication：接收并解析用户凭证。
 * successfulAuthentication：用户成功登录后，这个方法会被调用，我们在这个方法里生成token并返回。
 */
@Component
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private AuthenticationManager authenticationManager;

    public JwtAuthenticationFilter(AuthenticationManager authenticationManager){
        this.authenticationManager = authenticationManager;
        super.setAuthenticationManager(authenticationManager);
        super.setFilterProcessesUrl("/oauth/authentication");
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request,
                                                HttpServletResponse response) throws AuthenticationException {

        // 从输入流中获取到登录的信息
        try {
            LoginUserDO loginUser = new ObjectMapper().readValue(request.getInputStream(), LoginUserDO.class);
            return authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(loginUser.getUsername(), loginUser.getPassword())
            );
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }



    /**
     * 成功验证后调用的方法
     * 如果验证成功，就生成token并返回
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request,
                                            HttpServletResponse response,
                                            FilterChain chain,
                                            Authentication authResult) throws IOException{

        JwtUser jwtUser = (JwtUser) authResult.getPrincipal();
        System.out.println("jwtUser:" + jwtUser.toString());
        if(!jwtUser.isEnabled()) {
            this.unsuccessfulAuthentication(request, response, new DisabledException("账号不可用"));
            return;
        } else if(Objects.equals(jwtUser.getId(), 0L)) {
            this.unsuccessfulAuthentication(request, response, new BadCredentialsException("账号不可用"));
            return;
        }

        String role = "";
        Collection<? extends GrantedAuthority> authorities = jwtUser.getAuthorities();
        for (GrantedAuthority authority : authorities){
            role = authority.getAuthority();
        }

        String token = JwtUtils.createToken(jwtUser.getId(), jwtUser.getUsername(), role, jwtUser.getTenantId());
        // 返回创建成功的token
        // 但是这里创建的token只是单纯的token
        // 按照jwt的规定，最后请求的时候应该是 `Bearer token`
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        String tokenStr = JwtUtils.TOKEN_PREFIX + token;
        response.setHeader("token", tokenStr);
        response.getWriter().write(OpenGsonWrapper.toJson(new Response<>("success", "登录成功")));
    }

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        if(failed instanceof BadCredentialsException) {
            response.getWriter().write(OpenGsonWrapper.toJson(new Response<>(BizResultCode.LOGIN_ERROR)));
        } else if(failed instanceof DisabledException) {
            response.getWriter().write(OpenGsonWrapper.toJson(new Response<>(BizResultCode.ACCOUNT_EXCEPTION)));
        }

    }
}
